﻿using MyControlClassLibrary.Node;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MyControlClassLibrary
{
    public partial class CanvasControl : UserControl
    {
        //画布
        Graphics g;

        //像素点X坐标(左上角是原点)
        private int _movePointX = 0;
        //像素点Y坐标(左上角是原点)
        private int _movePointY = 0;

        //创建一个集合用来存储要绘制的节点属性
        public List<NodeClass> NodeList = new List<NodeClass>();
        //创就一个集合用来存储要绘制的形状
        public List<Shape> ShapeList = new List<Shape>();

        /// <summary>
        /// 创建变量，用来记录当前会知道的形状索引，提供给动画挨个绘制路局
        /// </summary>
        private int currentShapeIndex = 0;
        /// <summary>
        /// 定义变量，用来记录当前遍历到形状的第几个坐标点
        /// </summary>
        private int animationStep = 0;
        /// <summary>
        /// 定义一个定时器，用来控制动画的绘制
        /// </summary>
        private Timer animationTimer = null;
        /// <summary>
        /// 绘制模式:默认是无（用户可以在画布上绘制形状）
        /// </summary>
        private DrawingMode drawingMode = DrawingMode.None;

        /// <summary>
        /// 用来提供用户定义绘制模式下，记录用户绘制的路径点
        /// </summary>
        private List<Point> CustomPathPoint = new List<Point>();
        /// <summary>
        /// 用来记录用户绘制的路径的索引，用来区分不同的路径(第N个路径)
        /// </summary>
        private int CustomPathIndex = 0;
        /// <summary>
        /// 记录用户绘制的矩形的索引
        /// </summary>
        private int RectangleIndex = 0;

        /// <summary>
        /// 记录用户绘制的圆形的索引
        /// </summary>
        private int CircleIndex = 0;

        [Description("X坐标")]
        /// <summary>
        /// 坐标轴:X坐标(坐标点X的坐标)
        /// </summary>
        public int MovePointX
        {
            get
            {
                //由于是四象限坐标系，坐标原点在中心，需要进行转换: 当前像素点坐标-中心点坐标
                return _movePointX - this.Width / 2;
            }
            set
            {
                //由于是四象限坐标系，坐标原点在中心，需要进行转换: 当前像素点坐标+中心点坐标
                _movePointX = value + this.Width / 2;
                //重绘(强制控件刷新，因为我们更新了坐标我们需要刷新界面上显示的内容)
                this.Invalidate();
            }
        }


        [Description("Y坐标")]
        public int MovePointY
        {
            get
            {
                //由于是四象限坐标系，坐标原点在中心，需要进行转换: 当前像素点坐标-中心点坐标
                return this.Height / 2 - _movePointY;
            }
            set
            {
                //由于是四象限坐标系，坐标原点在中心，需要进行转换: 当前像素点坐标+中心点坐标
                _movePointY = this.Height / 2 - value;
                //重绘(强制控件刷新，因为我们更新了坐标我们需要刷新界面上显示的内容)
                this.Invalidate();
            }
        }

        /// <summary>
        /// 更新绑定的列表
        /// </summary>
        public event Action<List<Shape>> acUpdateListBox = null;

        public CanvasControl()
        {
            InitializeComponent();
            //弃用原有样式，使用用户自定义的样式
            this.SetStyle(ControlStyles.UserPaint, true);
            //禁止擦除背景(用户绘制的内容保留)
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            //设置调整大小的时候也重绘
            this.SetStyle(ControlStyles.ResizeRedraw, true);
            //双缓冲(用来处理绘制GDI+的绘制，减少闪烁的情况)
            this.SetStyle(ControlStyles.DoubleBuffer, true);
            //实例化定时器，用来控制动画的绘制(绑定方法)
            animationTimer = new Timer();
            animationTimer.Interval = 50;//设置定时器的时间间隔
            animationTimer.Tick += animationTimer_Tick;//绑定定时器的事件
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            //场景画布区域(指定在什么位置绘制)
            g = e.Graphics;


            //修改画笔颜色与宽度
            Pen pen = new Pen(Color.LightGray, 1);
            //设置为虚线的样式
            pen.DashStyle = DashStyle.Dash;

            //绘制X轴网格线，以坐标中心点为原点，向左右两边绘制
            for (int i = 0; i < this.Width / 2; i += 40)
            {
                g.DrawLine(pen, this.Width / 2 - i, 0, this.Width / 2 - i, this.Height);
                g.DrawLine(pen, this.Width / 2 + i, 0, this.Width / 2 + i, this.Height);
            }

            //绘制Y轴网格线，以坐标中心点为原点，向上下两边绘制
            for (int i = 0; i < this.Height / 2; i += 40)
            {
                g.DrawLine(pen, 0, this.Height / 2 - i, this.Width, this.Height / 2 - i);
                g.DrawLine(pen, 0, this.Height / 2 + i, this.Width, this.Height / 2 + i);
            }

            //画笔
            pen = new Pen(Color.DarkGray, 3);
            //箭头
            AdjustableArrowCap lineCap = new AdjustableArrowCap(5, 5, true);
            //设置线帽(直线终点)
            pen.CustomEndCap = lineCap;


            //绘制四象限坐标系=>X轴/Y轴
            g.DrawLine(pen, 0, this.Height / 2, this.Width, this.Height / 2);
            g.DrawLine(pen, this.Width / 2, this.Height, this.Width / 2, 0);

            //设置线帽(无)
            pen.CustomEndCap = new AdjustableArrowCap(0, 0, true);

            //绘制X轴刻度线，以坐标中心点为原点，向左右两边绘制
            for (int i = 0; i < this.Width / 2; i += 40)
            {
                g.DrawLine(pen, this.Width / 2 - i, this.Height / 2 - 5, this.Width / 2 - i, this.Height / 2 + 5);
                g.DrawLine(pen, this.Width / 2 + i, this.Height / 2 - 5, this.Width / 2 + i, this.Height / 2 + 5);
            }

            //绘制Y轴刻度线，以坐标中心点为原点，向上下两边绘制
            for (int i = 0; i < this.Height / 2; i += 40)
            {
                g.DrawLine(pen, this.Width / 2 - 5, this.Height / 2 - i, this.Width / 2 + 5, this.Height / 2 - i);
                g.DrawLine(pen, this.Width / 2 - 5, this.Height / 2 + i, this.Width / 2 + 5, this.Height / 2 + i);
            }


            //绘制刻度值的字体
            Font font = new Font("微软雅黑", 8);
            //绘制字体的颜色
            Brush brush = new SolidBrush(Color.DarkGray);
            //绘制X轴刻度值，以坐标中心点为原点，向左右两边绘制
            for (int i = 0; i < this.Width / 2; i += 40)
            {
                g.DrawString(i.ToString(), font, brush, this.Width / 2 + i, this.Height / 2 + 5);
                g.DrawString((-i).ToString(), font, brush, this.Width / 2 - i, this.Height / 2 + 5);
            }
            //绘制Y轴刻度值，以坐标中心点为原点，向上下两边绘制
            for (int i = 0; i < this.Height / 2; i += 40)
            {
                g.DrawString(i.ToString(), font, brush, this.Width / 2 - 20, this.Height / 2 - i);
                g.DrawString((-i).ToString(), font, brush, this.Width / 2 - 20, this.Height / 2 + i);
            }

            //绘制原点
            g.FillEllipse(new SolidBrush(Color.Red), this.Width / 2 - 4, this.Height / 2 - 4, 8, 8);


            //如果是动画模式，那么就不显示十字架，直到动画结束(由动画定时器控制十字架)
            if (animationTimer.Enabled == false)
            {
                //绘制十字架圆
                DrawCrossCircle(g, Pens.Red, _movePointX, _movePointY, 20, 20, 20);
            }
            else
            {

                //绘制当前进度的轨迹
                using (Pen p = new Pen(ShapeList[currentShapeIndex].Color, 4))
                {
                    for (int i = 0; i < animationStep; i++)
                    {
                        g.DrawLine(p, ShapeList[currentShapeIndex].TrajectoryPoint[i], ShapeList[currentShapeIndex].TrajectoryPoint[i + 1]);
                    }
                }

                //更新当前轨迹点的十字架
                Point currentPoint = ShapeList[currentShapeIndex].TrajectoryPoint[animationStep];
                //绘制十字架圆
                DrawCrossCircle(g, Pens.Red, currentPoint.X, currentPoint.Y, 20, 20, 20);
            }

            //绘制形状
            foreach (Shape shape in ShapeList)
            {
                shape.Draw(g);
            }

            //绘制节点
            foreach (NodeClass node in NodeList)
            {


                //由于是四象限坐标系，坐标原点在中心，需要进行转换，输入的是坐标系的坐标，我们需要转换成像素点的坐标
                int x = node.X + this.Width / 2;
                int y = this.Height / 2 - node.Y;

                //绘制圆的半径
                int R = 5;

                //绘制节点-圆形
                g.FillEllipse(new SolidBrush(node.NodeColor), x - R, y - R, 2 * R, 2 * R);

                //测量字符串的宽度与高度
                SizeF sizeF = g.MeasureString(node.NodeName, new Font("微软雅黑", 8));
                //绘制节点-名称(圆的上方居中绘制)
                g.DrawString(node.NodeName, new Font("微软雅黑", 8), Brushes.Black, x - sizeF.Width / 2, y - 20);

                //测量字符串的宽度与高度
                sizeF = g.MeasureString($"({node.X},{node.Y})", new Font("微软雅黑", 8));
                //绘制节点-坐标(圆的下方居中绘制)
                g.DrawString($"({node.X},{node.Y})", new Font("微软雅黑", 8), Brushes.Black, x - sizeF.Width / 2, y + 10);
            }

        }


        /// <summary>
        /// 绘制十字架圆
        /// </summary>
        /// <param name="g">画布</param>
        /// <param name="pen">画笔</param>
        /// <param name="x">X坐标</param>
        /// <param name="y">Y坐标</param>
        /// <param name="width">绘制矩形的宽(矩形中绘制内切圆)</param>
        /// <param name="height">绘制矩形的高(矩形中绘制内切圆)</param>
        /// <param name="length">十字架线段的长度</param>
        public void DrawCrossCircle(Graphics g, Pen pen, int x, int y, int width, int height, int length)
        {
            //中心点坐标
            Point CentPoint = new Point(x, y);

            //绘制十字架线程-1
            g.DrawLine(pen, CentPoint.X - length, CentPoint.Y, CentPoint.X + length, CentPoint.Y);
            g.DrawLine(pen, CentPoint.X, CentPoint.Y - length, CentPoint.X, CentPoint.Y + length);

            //绘制不填充的圆-外圆
            g.DrawEllipse(pen, CentPoint.X - width / 2, CentPoint.Y - height / 2, width, height);
            //绘制不填充的圆-内圆
            g.DrawEllipse(pen, CentPoint.X - width / 4, CentPoint.Y - height / 4, width / 2, height / 2);

            //绘制中心圆点
            g.FillEllipse(Brushes.Red, CentPoint.X - 2, CentPoint.Y - 2, 4, 4);

            //右下角显示坐标值
            g.DrawString($"({MovePointX},{MovePointY})", new Font("微软雅黑", 8), Brushes.Black, x + 10, y + 10);

        }
        /// <summary>
        /// 定时器的事件，用来控制动画的绘制
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void animationTimer_Tick(object sender, EventArgs e)
        {
            //获取当前绘制的形状
            Shape currentShape = ShapeList[currentShapeIndex];
            //逐渐递增，用来移动点的索引
            animationStep++;

            //如果当前轨迹绘制完成，那么就移动到下一个形状
            if (animationStep >= currentShape.TrajectoryPoint.Count)
            {
                //判断是否是最后一个形状，如果是最后一个形状，那么就停止定时器
                if (currentShapeIndex >= ShapeList.Count - 1)
                {
                    //停止定时器
                    animationTimer.Stop();
                    return;
                }
                //修改当前绘制的形状索引=>下一个形状
                currentShapeIndex++;
                //重置当前绘制的形状的坐标索引
                animationStep = 0;
            }
            //强制刷新
            this.Invalidate();
        }
        /// <summary>
        /// 启动绘制动画的定时器
        /// </summary>
        public void StartAnimation()
        {
            //重置当前绘制的形状索引
            currentShapeIndex = 0;
            //索引当前绘制形状的坐标索引
            animationStep = -1;
            //启动定时器
            animationTimer.Start();
        }
        /// <summary>
        /// 停止绘制动画的定时器
        /// </summary>
        public void StopAnimation()
        {
            //停止定时器
            animationTimer.Stop();
        }

        /// <summary>
        /// 鼠标按下
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CanvasControl_MouseDown(object sender, MouseEventArgs e)
        {
            //判断鼠标左键按下才进行处理
            if (e.Button == MouseButtons.Left)
            {
                //处理绘制的情况
                switch (this.drawingMode)
                {
                    case DrawingMode.None:
                        break;
                    case DrawingMode.Circle:
                        Circle circle = new Circle("圆形-" + CircleIndex, Color.SeaGreen, e.Location);
                        //添加到集合当中
                        ShapeList.Add(circle);
                        break;
                    case DrawingMode.Rectangle:
                        RectangleShape rectangleShape = new RectangleShape("矩形-" + RectangleIndex, Color.DarkCyan, e.Location);
                        //添加到集合当中
                        ShapeList.Add(rectangleShape);
                        break;
                    case DrawingMode.CustomPath:
                        //查找指定名称的自定义路径是否已经存在，如果存在就不添加，如果不存在就添加
                        foreach (Shape shape in ShapeList)
                        {
                            if (shape.Name == "自定义路径-" + CustomPathIndex)
                            {
                                return;
                            }
                        }
                        //添加用户绘制的路径
                        CustomPathShape customPathShape = new CustomPathShape("自定义路径-" + CustomPathIndex, Color.DarkCyan);
                        //添加坐标点
                        customPathShape.TrajectoryPoint.Add(e.Location);//记录下当前的坐标点
                        //添加到集合当中
                        ShapeList.Add(customPathShape);
                        break;
                }
            }
        }
        /// <summary>
        /// 鼠标移动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CanvasControl_MouseMove(object sender, MouseEventArgs e)
        {
            //只有才鼠标左键按下的情况下才进行处理
            if (e.Button == MouseButtons.Left)
            {
                switch (this.drawingMode)
                {
                    case DrawingMode.None:
                        break;
                    case DrawingMode.Circle:
                        //查找到对应名称的形状
                        Shape circleShape = ShapeList.FirstOrDefault(s => s.Name == "圆形-" + CircleIndex);
                        //如果找到了对应的形状，那么就更新坐标点
                        circleShape?.Upadate(e.Location);//记录下当前的坐标点
                        break;
                    case DrawingMode.Rectangle:
                        //查找到对应名称的形状
                        Shape rectangleShape = ShapeList.FirstOrDefault(s => s.Name == "矩形-" + RectangleIndex);
                        //如果找到了对应的形状，那么就更新坐标点
                        rectangleShape?.Upadate(e.Location);//记录下当前的坐标点
                        break;
                    case DrawingMode.CustomPath:
                        //查找到对应名称的形状 Shape customPathShape = ShapeList.FirstOrDefault(s => s.Name == "自定义路径-" + CustomPathIndex);
                        // Foreach循环遍历
                        Shape customPathShape = null;
                        foreach (Shape shape in ShapeList)
                        {
                            if (shape.Name == "自定义路径-" + CustomPathIndex)
                            {
                                customPathShape = shape;
                                break;
                            }
                        }
                        //如果找到了对应的形状，那么就添加坐标点
                        customPathShape?.Upadate(e.Location);//记录下当前的坐标点
                        break;
                }
            }
            //强制刷新界面
            this.Invalidate();
        }
        /// <summary>
        /// 鼠标抬起
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CanvasControl_MouseUp(object sender, MouseEventArgs e)
        {
            switch (this.drawingMode)
            {
                case DrawingMode.None:
                    break;
                case DrawingMode.Circle:
                    CircleIndex++;//抬起鼠标，那么就是下一个圆形了，所以索引+1
                    break;
                case DrawingMode.Rectangle:
                    RectangleIndex++;//抬起鼠标，那么就是下一个矩形了，所以索引+1
                    break;
                case DrawingMode.CustomPath:
                    CustomPathIndex++;//抬起鼠标，那么就是下一个路径了，所以索引+1
                    break;
            }
            //更新形状列表
            acUpdateListBox?.Invoke(ShapeList);
            //强制刷新界面
            this.Invalidate();

        }
        /// <summary>
        /// 设置绘制模式或者清除绘制模式
        /// </summary>
        /// <param name="drawingMode"></param>
        public void SetDrawingMode(DrawingMode drawingMode)
        {
            this.drawingMode = drawingMode;
        }



    }
}
